package com.crazymakercircle.zk.basicOperate;

import com.crazymakercircle.util.Logger;
import com.crazymakercircle.zk.ClientFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.utils.CloseableUtils;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.junit.Test;

import java.util.List;

/**
 * create by 尼恩 @ 疯狂创客圈
 **/
@Slf4j
public class CRUD {
    //本机
    private static final String ZK_ADDRESS = "127.0.0.1:2181";

    //虚拟机
    //private static final String ZK_ADDRESS = "cdh1:2181";

    /**
     * 检查节点
     */
    @Test
    public void checkNode() {
        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {
            //启动客户端实例,连接服务器
            client.start();

            // 创建一个 ZNode 节点
            // 节点的数据为 payload

            String zkPath = "/test/CRUD/remoteNode-1";
            long stime = System.currentTimeMillis();
            Stat stat = client.checkExists().forPath(zkPath);
            if (null == stat) {
                log.info("节点不存在:" + zkPath);
            } else {

                log.info("节点存在 stat is:" + stat.toString());//节点存在 stat is:38654705730,38654705730,1619698547306,1619698547306,0,0,0,0,5,0,38654705730
            }
            Logger.info("forPath耗费时间ms："+ (System.currentTimeMillis()-stime));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }


    /**
     * 创建节点 通过Curator创建ZNode节点
     * 通过Curator框架创建ZNode节点，可使用create()方法。create()方法不需要传入ZNode的节点路径，所以并不会立即创建节点，
     * 仅仅返回一个CreateBuilder构造者实例。通过该CreateBuilder构造者实例，可以设置创建节点时的一些行为参数，
     * 最后再通过构造者实例的forPath(String znodePath, byte[] payload)方法来完成真正的节点创建。
     */
    @Test
    public void createNode() {

        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {
            //启动客户端实例,连接服务器
            client.start();

            // 创建一个 ZNode 节点
            // 节点的数据为 payload
            //ZooKeeper节点有4种类型：（1）PERSISTENT   持久化节点（2）PERSISTENT_SEQUENTIAL   持久化顺序节点（3）PHEMERAL   临时节（4）EPHEMERAL_SEQUENTIAL     临时顺序节点
            String data = "hello";
            byte[] payload = data.getBytes("UTF-8");
            String zkPath = "/test/CRUD/remoteNode-1";
            long stime = System.currentTimeMillis();
            client.create()
                    .creatingParentsIfNeeded()
                    .withMode(CreateMode.PERSISTENT)
                    .forPath(zkPath, payload);//创建了11个节点,UTF-8的byte[]字节是5个?,
            Logger.info("forPath耗费时间ms：" + (System.currentTimeMillis() - stime));

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }


    /**
     * 创建 临时 节点
     */
    @Test
    public void createEphemeralNode() {
        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {
            //启动客户端实例,连接服务器
            client.start();

            // 创建一个 ZNode 节点
            // 节点的数据为 payload

            String data = "hello";
            byte[] payload = data.getBytes("UTF-8");
            String zkPath = "/test/remoteNode-1";
            client.create()
                    .creatingParentsIfNeeded()

                    .withMode(CreateMode.EPHEMERAL)
                    .forPath(zkPath, payload);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    /**
     * 创建 持久化 顺序 节点
     */
    @Test
    public void createPersistentSeqNode() {
        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {
            //启动客户端实例,连接服务器
            client.start();

            // 创建一个 ZNode 节点
            // 节点的数据为 payload

            String data = "hello";

            for (int i = 0; i < 10; i++) {
                byte[] payload = data.getBytes("UTF-8");
                String zkPath = "/test/remoteNode-seq-";
                client.create()
                        .creatingParentsIfNeeded()
                        .withMode(CreateMode.PERSISTENT_SEQUENTIAL)
                        .forPath(zkPath, payload);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    /**
     * 读取节点
     * 在Curator框架中，与节点读取的有关的方法主要有三个：
     * （1）首先是判断节点是否存在，调用checkExists方法。
     * （2）其次是获取节点的数据，调用getData方法。
     * （3）最后是获取子节点列表，调用getChildren方法。
     */
    @Test
    public void readNode() {
        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {
            //启动客户端实例,连接服务器
            client.start();

            String zkPath = "/test/CRUD/remoteNode-1";


            Stat stat = client.checkExists().forPath(zkPath);
            if (null != stat) {
                //读取节点的数据
                byte[] payload = client.getData().forPath(zkPath);
                String data = new String(payload, "UTF-8");
                log.info("read data:" + data);

                String parentPath = "/test";
                List<String> children = client.getChildren().forPath(parentPath);
                //CURD是上级节点,上级节点下面有10个节点
                //20:16:59.656 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000005
                //20:16:59.656 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000006
                //20:16:59.656 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:CRUD
                //20:16:59.656 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000007
                //20:16:59.656 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000008
                //20:16:59.657 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000009
                //20:16:59.657 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000000
                //20:16:59.657 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000001
                //20:16:59.657 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000002
                //20:16:59.657 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000003
                //20:16:59.657 [main] INFO com.crazymakercircle.zk.basicOperate.CRUD - child:remoteNode-seq-0000000004
                for (String child : children) {
                    log.info("child:" + child);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }


    /**
     * 更新节点.节点的更新分为同步更新与异步更新。同步更新就是更新线程是阻塞的，一直阻塞到更新操作执行完成为止。调用setData()方法进行同步更新
     */
    @Test
    public void updateNode() {
        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {
            //启动客户端实例,连接服务器
            client.start();


            String data = "hello world";
            byte[] payload = data.getBytes("UTF-8");
            String zkPath = "/test/remoteNode-1";
            client.setData()
                    .forPath(zkPath, payload);


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }


    /**
     * 更新节点 - 异步模式
     */
    @Test
    public void updateNodeAsync() {
        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {

            //更新完成监听器
            AsyncCallback.StringCallback callback = new AsyncCallback.StringCallback() {

                @Override
                public void processResult(int i, String s, Object o, String s1) {
                    System.out.println(
                            "i = " + i + " | " +
                                    "s = " + s + " | " +
                                    "o = " + o + " | " +
                                    "s1 = " + s1
                    );
                }
            };
            //启动客户端实例,连接服务器
            client.start();

            String data = "hello ,every body! ";
            byte[] payload = data.getBytes("UTF-8");
            String zkPath = "/test/CRUD/remoteNode-1";
            client.setData()
                    .inBackground(callback)
                    .forPath(zkPath, payload);

            Thread.sleep(10000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }

    /**
     * 删除节点
     * 在Curator中删除节点删除节点非常简单，只需调用delete方法
     */
    @Test
    public void deleteNode() {
        //创建客户端
        CuratorFramework client = ClientFactory.createSimple(ZK_ADDRESS);
        try {
            //启动客户端实例,连接服务器
            client.start();

            //删除节点
            String zkPath = "/test/CRUD/remoteNode-1";
            //删除节点前先检查节点是否存在
            Stat stat = client.checkExists().forPath(zkPath);
            if (stat != null) {//stat部位null表示节点存在
                client.delete().forPath(zkPath);
            }

            //删除后查看结果
            String parentPath = "/test";
            List<String> children = client.getChildren().forPath(parentPath);

            for (String child : children) {
                log.info("child:" + child);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeQuietly(client);
        }
    }


}
